package com.lfz.demo.controller;

import cn.hutool.core.lang.Console;
import com.lfz.demo.common.enums.CensusTimeEnum;
import com.lfz.demo.common.enums.LogType;
import com.lfz.demo.common.enums.MenuType;
import com.lfz.demo.common.target.HoneyLogs;
import com.lfz.demo.entity.ChinaPopulation;
import com.lfz.demo.service.IndexService;
import com.lfz.demo.utils.RedisUtil;
import com.lfz.demo.vo.ChinaPopulationDataVo;
import com.lfz.demo.vo.DataView;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author lfz
 * @date 2025/2/25 19:08
 */
@RestController
@RequestMapping("/china")
public class ChinaDataAdminController {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
    // 缓存时间常量（单位：分钟）
    private static final String CACHE_PREFIX = "chinaManage:"; //缓存键前缀，避免键冲突。

    @Autowired
    private IndexService indexService;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 模糊查询 带有分页
     * @param chinaPopulationDataVo
     * @return
     */
    @GetMapping("/listDataByPage")
    public DataView listDataByPage(ChinaPopulationDataVo chinaPopulationDataVo){
        String cacheKey = CACHE_PREFIX + String.format("china:list:%s:%s:%d:%d",
                chinaPopulationDataVo.getCityName(),
                chinaPopulationDataVo.getCensusTime(),
                chinaPopulationDataVo.getPage(),
                chinaPopulationDataVo.getLimit());

        try {
            DataView cachedData = (DataView) redisUtil.get(cacheKey);
            if (cachedData != null) {
                return cachedData;
            }
        } catch (Exception e) {
            logger.error("Redis反序列化失败，降级查询MySQL", e);
        }

        // MySQL查询逻辑...
        // 创建分页对象
        Pageable pageable = PageRequest.of(chinaPopulationDataVo.getPage() - 1, chinaPopulationDataVo.getLimit());

        // 创建查询条件
        Specification<ChinaPopulation> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (chinaPopulationDataVo.getCityName() != null) {
                predicates.add(criteriaBuilder.like(root.get("cityName"), "%" + chinaPopulationDataVo.getCityName() + "%"));
            }
            if (chinaPopulationDataVo.getCensusTime() != null) {
                // 使用 equal 方法进行精确匹配
                predicates.add(criteriaBuilder.equal(root.get("censusTime"), chinaPopulationDataVo.getCensusTime()));
            }
            query.orderBy(criteriaBuilder.desc(root.get("updateTime")));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };

        // 分页查询
        Page<ChinaPopulation> page = indexService.findByPage(spec, pageable);

        // 返回数据封装
        DataView dataView = new DataView(page.getTotalElements(), page.getContent());

        try {
            redisUtil.set(cacheKey, dataView, 60); // 缓存60分钟
        } catch (Exception e) {
            logger.error("缓存设置失败，但不影响业务流程", e);
        }

        return dataView;
    }

    /**
     * 根据Id删除数据
     * @param id
     * @return
     */
    @HoneyLogs(operation = "删除",type = LogType.DELETE,menu = MenuType.ChinaManager)
    @PostMapping("/deleteById")
    public DataView deleteById(Integer id){
        indexService.removeById(id);
        DataView dataView = new DataView();
        // 清理缓存
        clearRelatedCaches();
        dataView.setCode(200);
        dataView.setMsg("删除中国人口地图数据成功");
        return dataView;
    }

    /**
     * 批量删除数据
     * @param ids
     * @return
     */
    @HoneyLogs(operation = "批量删除", type = LogType.BATCH_DELETE, menu = MenuType.ChinaManager)
    @PostMapping("/batchDelete")
    public DataView batchDelete(@RequestBody List<Integer> ids) {
        try {
            indexService.removeByIds(ids);
            DataView dataView = new DataView();
            // 清理缓存
            clearRelatedCaches();
            dataView.setCode(200);
            dataView.setMsg("批量删除中国人口地图数据成功");
            return dataView;
        } catch (Exception e) {
            e.printStackTrace();
            DataView dataView = new DataView();
            dataView.setCode(500);
            dataView.setMsg("批量删除失败：" + e.getMessage());
            return dataView;
        }
    }

    /**
     * 新增人口数据 Or 通过【Id】修改人口数据 (更新)
     * insert into nocv_data(city_name, population) values("",?)
     * update nocv_data set city_name = "", population = ? where id = ?
     * @param nocvData
     * @return
     */
    @HoneyLogs(operation = "更新", type = LogType.AddOrUpdate, menu = MenuType.ChinaManager)
    @PostMapping("/addOrUpdateChina")
    public DataView addOrUpdateChina(ChinaPopulation nocvData){
        boolean b = indexService.saveOrUpdate(nocvData);
        DataView dataView = new DataView();
        // 更新操作
        if (b){
            // 删除所有相关的缓存
            clearRelatedCaches();
            dataView.setCode(200);
            dataView.setMsg("更新中国人口地图数据成功！");
        } else {
            dataView.setCode(100);
            dataView.setMsg("更新中国人口地图数据失败！");
        }
        return dataView;
    }

    /**
     * Excel 的拖拽或者点击上传
     * 1、前台页面发送一个请求，上传文件MutilpartFile Http
     * 2、Controller，上传文件MutilpartFile 参数
     * 3、POI 解析文件，里面的数据一行一行的全部解析出来
     * 4、每一条数据插入数据库
     *
     * @RequestParam 接收数据参数
     */
    @HoneyLogs(operation = "导入", type = LogType.INPUT, menu = MenuType.ChinaManager)
    @PostMapping("/excelImportChina")
    public DataView excelImportChina(@RequestParam("file") MultipartFile file) throws IOException {
        DataView dataView = new DataView();
        if (file.isEmpty()) {
            dataView.setMsg("上传文件为空，无法上传！");
            return dataView;
        }

        Workbook wb = null;
        try {
            String fileName = file.getOriginalFilename();
            if (fileName == null || !(fileName.endsWith(".xls") || fileName.endsWith(".xlsx"))) {
                dataView.setMsg("不支持的文件格式，请上传 .xls 或 .xlsx 文件！");
                return dataView;
            }

            wb = fileName.endsWith(".xlsx") ?
                    new XSSFWorkbook(file.getInputStream()) :
                    new HSSFWorkbook(file.getInputStream());

            Sheet sheet = wb.getSheetAt(0);
            List<ChinaPopulation> list = new ArrayList<>();

            for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;

                ChinaPopulation china = new ChinaPopulation();

                // 城市名称（第 1 列，STRING）
                china.setCityName((String) getCellValue(row.getCell(0)));

                // 常住人口（第 2 列，NUMERIC）
                china.setPopulation(getNumericCellValue(row.getCell(1)));

                // 出生人口（第 3 列，NUMERIC）
                china.setBirthNum(getNumericCellValue(row.getCell(2)));

                // 死亡人口（第 4 列，NUMERIC）
                china.setDeadNum(getNumericCellValue(row.getCell(3)));

                // 性别比（第 5 列，STRING 或 NUMERIC）
                Cell sexRatioCell = row.getCell(4);
                Double sexRatio = null;
                if (sexRatioCell != null) {
                    Object value = getCellValue(sexRatioCell);
                    if (value instanceof Number) {
                        sexRatio = ((Number) value).doubleValue();
                    } else if (value instanceof String) {
                        try {
                            sexRatio = Double.parseDouble((String) value); // 字符串转数值
                        } catch (NumberFormatException e) {
                            // 处理无效格式（如非数字字符串）
                            dataView.setMsg("第 " + (i + 1) + " 行性别比格式错误：" + value);
                            return dataView;
                        }
                    }
                }
                china.setSexRatio(sexRatio); // 允许 null（数据库字段需 nullable）

                // 统计时间（第6列，DATE）
                Cell updateTimeCell = row.getCell(5);
                java.sql.Date sqlDate = null;
                if (updateTimeCell != null) {
                    Object value = getCellValue(updateTimeCell);
                    if (value instanceof Date) {
                        sqlDate = new java.sql.Date(((Date) value).getTime());
                    } else if (value instanceof String) {
                        String dateStr = (String) value;
                        if (!dateStr.isEmpty()) {
                            try {
                                // 支持多种日期格式（如 "2020/11/1", "2020-11-01"）
                                SimpleDateFormat[] formats = {
                                        new SimpleDateFormat("yyyy/MM/dd"),
                                        new SimpleDateFormat("yyyy-MM-dd")
                                };
                                for (SimpleDateFormat sdf : formats) {
                                    try {
                                        sqlDate = new java.sql.Date(sdf.parse(dateStr).getTime());
                                        break;
                                    } catch (ParseException e) {
                                        // 继续尝试下一种格式
                                    }
                                }
                                if (sqlDate == null) {
                                    throw new IllegalArgumentException("无效的日期格式：" + dateStr);
                                }
                            } catch (Exception e) {
                                dataView.setMsg("第 " + (i + 1) + " 行统计时间格式错误：" + dateStr);
                                return dataView;
                            }
                        }
                    }
                }

                // 普查时间（第 7 列，自定义处理）
                // 普查时间（枚举类型）
                Cell censusTimeCell = row.getCell(6);
                Object censusValue = getCellValue(censusTimeCell);
                if (censusValue instanceof String) {
                    String censusLabel = (String) censusValue;
                    try {
                        china.setCensusTime(CensusTimeEnum.fromDesc(censusLabel));
                    } catch (IllegalArgumentException e) {
                        dataView.setMsg("第 " + i + " 行错误：" + e.getMessage());
                        return dataView;
                    }
                } else {
                    dataView.setMsg("第 " + i + " 行普查时间格式错误，必须为字符串");
                    return dataView;
                }

                list.add(china);
            }

            indexService.saveBatch(list);
            // 清理缓存
            clearRelatedCaches();
            dataView.setCode(200);
            dataView.setMsg("小可爱！！Excel 文件被你上传并解析成功！");

        } catch (IOException e) {
            dataView.setMsg("文件读取失败：" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (wb != null) {
                try { wb.close(); } catch (IOException e) { e.printStackTrace(); }
            }
        }
        return dataView;
    }

    // 通用单元格解析方法
    private Object getCellValue(Cell cell) {
        if (cell == null) return null;
        switch (cell.getCellType()) {
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue();
                } else {
                    return cell.getNumericCellValue();
                }
            case STRING:
                String value = cell.getStringCellValue().trim();
                // 尝试解析字符串为日期
                try {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    return sdf.parse(value);
                } catch (ParseException e) {
                    // 不是日期格式，按原逻辑处理
                    return value;
                }
            case BOOLEAN:
                return cell.getBooleanCellValue();
            case BLANK:
                return null;
            default:
                return null;
        }
    }

    // 辅助方法：获取数值类型单元格的值（处理空值）
    private int getNumericCellValue(Cell cell) {
        if (cell == null) return 0;
        Object value = getCellValue(cell);
        return value instanceof Number ? ((Number) value).intValue() : 0;
    }

    /**
     * 导出 Excel文件    中国人口数据
     * 1、查询数据库，拿到所有数据
     * 2、建立Excel对象，封装数据
     * 3、建立输出流，输出文件
     */
    @HoneyLogs(operation = "导出", type = LogType.OUTEXCEL, menu = MenuType.ChinaManager)
    @GetMapping("/excelOutPutChina")
    public void excelOutPutChina(@RequestParam("fileName") String fileName ,HttpServletResponse response) {
        try (XSSFWorkbook wb = new XSSFWorkbook();
             OutputStream os = response.getOutputStream()) {

            // 1. 基础配置
            // 解码文件名（处理前端的encodeURIComponent）
            String decodedFileName = URLDecoder.decode(fileName, "UTF-8");

            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("UTF-8");
            // 设置响应头（标准RFC 5987格式）
            response.setHeader("Content-Disposition",
                    "attachment; filename*=utf-8''" + URLEncoder.encode(decodedFileName, "UTF-8"));


            // 2. 创建表格
            XSSFSheet sheet = wb.createSheet("中国人口数据");
            createHeader(sheet); // 独立表头创建方法

            // 3. 获取数据（添加空列表处理）
            List<ChinaPopulation> list = indexService.list();
            if (list.isEmpty()) {
                response.setStatus(HttpServletResponse.SC_NO_CONTENT);
                return;
            }

            // 4. 数据格式化工具（线程安全）
            ThreadLocal<SimpleDateFormat> dateFormatter = ThreadLocal.withInitial(
                    () -> new SimpleDateFormat("yyyy/MM/dd")
            );

            // 5. 填充数据（带异常隔离）
            for (int i = 0; i < list.size(); i++) {
                try {
                    ChinaPopulation data = list.get(i);
                    XSSFRow row = sheet.createRow(i + 1);
                    fillRow(row, data, dateFormatter.get());
                } catch (Exception e) {
                    Console.error("第 " + (i + 1) + " 行数据填充失败：", e);
                    // 可选择跳过该行或终止导出（根据业务需求）
                }
            }

            // 6. 优化设置
            autoSizeColumns(sheet, 0, 6); // 自动调整列宽（0-6列）
            wb.write(os);
            os.flush();

        } catch (IOException e) {
            handleExportError(response, "文件写入失败：" + e.getMessage());
            Console.error("导出IO错误：", e);
        } catch (Exception e) {
            handleExportError(response, "数据处理失败：" + e.getMessage());
            Console.error("导出逻辑错误：", e);
        }
    }

    // 独立方法：创建表头
    private void createHeader(XSSFSheet sheet) {
        XSSFRow header = sheet.createRow(0);
        String[] headers = {
                "省份名称", "常住人口", "出生人口", "死亡人口",
                "性别比(女=100)", "统计时间", "普查时间"
        };
        for (int i = 0; i < headers.length; i++) {
            header.createCell(i).setCellValue(headers[i]);
        }
    }

    // 独立方法：填充行数据（带类型检查）
    private void fillRow(XSSFRow row, ChinaPopulation data, SimpleDateFormat dateFormat) {
        // 省份名称（字符串）
        setCellValue(row, 0, data.getCityName(), String.class);

        // 数值类型（人口数据）
        setCellValue(row, 1, data.getPopulation(), Integer.class);
        setCellValue(row, 2, data.getBirthNum(), Integer.class);
        setCellValue(row, 3, data.getDeadNum(), Integer.class);

        // 性别比（字符串）
        setCellValue(row, 4, data.getSexRatio(), Double.class);

        // 统计时间（日期）
        // 在 fillRow 方法中修改日期处理部分
        if (data.getUpdateTime() != null) {
            java.sql.Date sqlDate = new java.sql.Date(data.getUpdateTime().getTime());
            setCellValue(row, 5, sqlDate, java.sql.Date.class, dateFormat);
        } else {
            setCellValue(row, 5, null, java.sql.Date.class, dateFormat);
        }

        // 普查时间（枚举标签）
        setCellValue(row, 6, data.getCensusTime(), CensusTimeEnum.class);
    }

    // 通用单元格设置（带类型校验）
    private <T> void setCellValue(XSSFRow row, int column, T value, Class<T> clazz) {
        XSSFCell cell = row.createCell(column);
        if (value == null) {
            cell.setCellType(CellType.BLANK);
            return;
        }

        if (clazz.isAssignableFrom(String.class)) {
            cell.setCellValue((String) value);
        } else if (clazz.isAssignableFrom(Double.class)) {
            cell.setCellValue((Double) value);
        } else if (clazz.isAssignableFrom(Integer.class)) {
            cell.setCellValue((Integer) value);
        } else if (clazz.isAssignableFrom(java.util.Date.class)) {
            // 假设这里有日期格式化的逻辑
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            cell.setCellValue(dateFormat.format((java.util.Date) value));
        } else if (clazz.isAssignableFrom(CensusTimeEnum.class)) {
            cell.setCellValue(((CensusTimeEnum) value).getDesc());
        } else {
            throw new IllegalArgumentException("不支持的类型：" + clazz.getName());
        }
    }

    private <T> void setCellValue(XSSFRow row, int column, T value, Class<T> clazz, SimpleDateFormat dateFormat) {
        XSSFCell cell = row.createCell(column);
        if (value == null) {
            cell.setCellType(CellType.BLANK);
            return;
        }

        if (clazz.isAssignableFrom(String.class)) {
            cell.setCellValue((String) value);
        } else if (clazz.isAssignableFrom(Integer.class)) {
            cell.setCellValue((Integer) value);
        } else if (clazz.isAssignableFrom(Date.class)) {
            cell.setCellValue(dateFormat.format((Date) value));
        } else if (clazz.isAssignableFrom(CensusTimeEnum.class)) {
            cell.setCellValue(((CensusTimeEnum) value).getDesc());
        } else {
            throw new IllegalArgumentException("不支持的类型：" + clazz.getName());
        }
    }

    // 独立方法：自动调整列宽
    private void autoSizeColumns(XSSFSheet sheet, int... columns) {
        Arrays.stream(columns).forEach(sheet::autoSizeColumn);
    }

    // 独立方法：错误处理
    private void handleExportError(HttpServletResponse response, String message) {
        try {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.setContentType("text/plain; charset=utf-8");
            try (OutputStream os = response.getOutputStream()) {
                os.write(message.getBytes(StandardCharsets.UTF_8));
                os.flush();
            }
        } catch (IOException e) {
            Console.error("错误信息写入失败：", e);
        }
    }

    /**
     * 清理相关缓存
     */
    private void clearRelatedCaches() {
        try {
            redisUtil.deletePattern(CACHE_PREFIX + "*");
            logger.info("成功清理日志相关缓存");
        } catch (Exception e) {
            logger.error("缓存清理失败", e);
        }
    }
}
